<html>
<head>
<title>Cellular Automata</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style>

.fig {
	margin: .5em;
	float: left;
	padding: 1em;
	border: 1px black solid;
}

</style>
<script type="text/javascript" src="jquery.js"></script> 
</head>
<body>
<script type="text/javascript">

function LineText() {
	this.lines = [];
	this.length = 0;
}

//文字列を、指定した回数だけ繰り返して連結する
//参考URL: http://snipplr.com/view/699/stringrepeat/
String.prototype.repeat = function(num) {
 for (var i=0, buf=''; i<num; i++) buf += this;
 return buf;
}

//文字列の左側を、指定した桁数になるまでcharで埋める
//参考URL: http://negisio.net/?p=370
String.prototype.pad = function(col, char) {
 col  = col  || 2;
 char = char || '0';
 if (this.length >= col) return this;
 return (char.repeat(col) + this).slice(-col);
 // 下の記述でも同じだが、わずかに遅い
 // return char.repeat(col - this.length) + this;
}

LineText.prototype = {
	add: function(lines) {
		if (this.lines.length < lines.length) {
			for (var i = this.lines.length; i < lines.length; i++) {
				this.lines[i] = " ".repeat(this.length);
			}
		} else if (this.lines.length > lines.length) {
			for (var i = lines.length; i < this.lines.length; i++) {
				lines[i] = "";
			}
		}
		var max = 0;
		for (var i = 0; i < lines.length; i++) {
			this.lines[i] += lines[i];
			if (max < this.lines[i].length) {
				max = this.lines[i].length;
			} 
		}
		for (var i = 0; i < lines.length; i++) {
			if (max > this.lines[i].length) {
				this.lines[i] += " ".repeat(max - this.lines[i].length);
			} 
		}
		this.length = max;
	},
};




function text(n) {
	return onText(n, 8) +
		onText(n, 1) + onText(n, 5) + "<br/>" +
		onText(n, 4) + "+" + onText(n, 2) + "<br/>" +
		onText(n, 7) + onText(n, 3) + onText(n, 6);
	
}
function getLineText(n) {
	return [onText(n, 8) + onText(n, 1) + onText(n, 5),
		onText(n, 4) + "+" + onText(n, 2),
		onText(n, 7) + onText(n, 3) + onText(n, 6)];
}

function onText(n, b) {
	return isOn(n, b) ? "O" : ".";
}

var BIT = [0, 1, 2, 4, 8, 16, 32, 64, 128];
function isOn(n, b) {
	return 0 < (n & BIT[b]);
}


function hex(n) {
	var t = n.toString(16);
	return "00".substr(t.length) + t;
}

function pattern() {
	var p = [];
	var result = [];
	var count = 0;
	for (var i = 0; i < 256; i++) {
		if (null == p[i]) {
			var r = i;
			var f = flip(i);
			for (var j = 0; j < 4; j++) {
				p[r] = true;
				p[f] = true;
				r = rotate(r);
				f = rotate(f);
			}
			result[count++] = i;
		}		
	}
	return result;	
}

var p = pattern();

function total(number, until) {
	var c = 0;
	for (var i = 1; i <= until; i++) {
		c += (0 < (number & BIT[i])) ? 1 : 0;
	}
	return c;
}

function total8(number) {
	return total(number, 8);
}
	
function total4(number) {
	return total(number, 4);
}

function rotate(n) {
	return ((n & 0x70) << 1) |
		((n & 0x80) >> 3) |
		((n & 0x7) << 1) |
		((n & 0x8) >> 3);
}

function flip(n) {
	return ((n & 0xa) |
		((n & 0xa0) >> 1) |
		((n & 0x50) << 1) |
		(n & 0x4) >> 2) |
		((n & 0x1) << 2);
}



/*
 * n0fa0.x0f.t3
 */

document.writeln([] instanceof Array);
document.writeln(isNaN("r"));

function TestUniverse(status, neighborhood) {
	this.status = status;
	this.neighborhood = neighborhood;
}

TestUniverse.prototype = {
	get: function(x, y) {
		return this.status;
	},
	getNeiborhood: function(x, y) {
		return this.neighborhood;
	},
}

/*
 * 
 */
var BIT = [0, 1, 2, 4, 8, 16, 32, 64, 128];
var EIGHT_SUM = [];
var FOUR_SUM = [];
var EIGHT_SUM_NUMBERS = [];
var FOUR_SUM_NUMBERS = [];

for (var n = 0; n < 256; n++) {
	var t8 = total8(n);
	var t4 = total4(n);
	EIGHT_SUM[n] = t8;
	FOUR_SUM[n] = t4;

	if (null == EIGHT_SUM_NUMBERS[t8]) {
		EIGHT_SUM_NUMBERS[t8] = [];
	}
	if (null == FOUR_SUM_NUMBERS[t4]) {
		FOUR_SUM_NUMBERS[t4] = [];
	}
	EIGHT_SUM_NUMBERS[t8].push(n);
	FOUR_SUM_NUMBERS[t4].push(n);
}

// calc(x, y, universe)
function NextStatusTable(table) {
	var t = {};
	for (var status in table) {
		var value = table[status];
		if (isNaN(value)) {
			t[status] = createNextStatus(value);
		} else {
			t[status] = new FixedStatus(value);
		}
	}
	this.table = t;
}

NextStatusTable.prototype = {
	next: function(x, y, universe) {
		var status = universe.get(x, y);
		if (this.table[status]) {
			return this.table[status].next(x, y, universe);
		} else {
			return 0;
		}
	},
};

/**
 * 
 * Interface
 * Filter.filter(status)
 * Returns: true or false
 */

/**
 * SelectFilter([1, 2])
 */
function SelectFilter(statuses) {
	var s = {};
	for (var i in statuses) {
		s[i] = true;
	} 
	this.statuses = s; 
}

SelectFilter.prototype = {
	filter: function(status) {
		return null != this.status;
	},
}

/**
 * FixedFilter(1)
 */
function FixedFilter(status) {
	this.status = status;
}

FixedFilter.prototype = {
	filter: function(status) {
		return this.status == status;
	},
};

/**
 * MoreThanZeroFilter
 */
var moreThanZeroFilter = {
	filter: function(status) {
		return 0 < status;
	},
};

var onlyOneFilter = new FixedFilter(1);

function toArray(value) {
	if (value instanceof Array) {
		return value;
	}
	return [value];
}

function createNextStatus(condition) {
	statuses = [];
	filter = onlyOneFilter;	
	for (var key in condition) {
		var value = condition[nextStatus];
		if ('f' == key) {
			if (value instanceof Array) {
				filter = SelectFilter(value);
			} else {
				filter = FixedFilter(value);
			}
		} else {
			var sf = new StatusFactory();
			var values = str.split(".");
			for (var i in values) {
				var v = values[i];
				var first = v.charAt(0);
				if ("t" == first) {
					for (var j = 1; j < v.length; j++) {
						sf.setTotal8(nextStatus, v.charAt(j));
					}
				} else if ("x" == first) {
					for (var j = 1; j < v.length; j+=2) {
						sf.setFlipRotate(nextStatus, parseInt(v.substr(j, 2), 16));
					}
				} else if ("n" == first) {
					for (var j = 1; j < v.length; j+=2) {
						sf.set(nextStatus, parseInt(v.substr(j, 2), 16));
					}
				} 
			}
			statuses[key] = sf.statuses;
		}
	}
	return new NextStatus(statuses, filter);
}

function StatusFactory() {
	this.statuses = [];
}

StatusFactory.protype = {
	set: function(nextStatus, neighborhood) {
		neighborhood = toArray(neighborhood);
		for (var i in neighborhood) {
			var n = neighborhood[i];
			this.statuses[n] = nextStatus;
		}
	},
	// totals = [1,2,3]
	setTotal8: function(nextStatus, total) {
		total = toArray(total);
		for (var i in total) {
			var t = total[i];
			for (j in EIGHT_SUM_NUMBERS[t]) {
				var n = EIGHT_SUM_NUMBERS[t][j];
				this.statuses[n] = nextStatus;
			}
		}
	},
	setFlipRotate: function(nextStatus, neighborhood) {
		neighborhood = toArray(neighborhood);
		for (var i in neighborhood) {
			var n = neighborhood[i];
			var r = n;
			var f = flip(n);
			for (var j = 0; j < 4; j++) {
				this.statuses[r] = nextStatus;
				this.statuses[f] = nextStatus;
				r = rotate(r);
				f = rotate(f);
			}
		}
	},
}

function NextStatus(statuses, filter) {
	this.statuses = statuses;
	this.filter = filter;
}
 
NextStatus.prototype = {
	next: function(x, y, universe) {
		var neighborhood = universe.getNeiborhood(x, y, this.filter);
		return this.statuses[neighborhood] || 0;
	},
}

function FixedStatus(status) {
	this.fixedStatus = status;
}

FixedStatus.prototype = {
	next: function(x, y, universe) {
		return this.fixedStatus;
	},
}


var lt = new LineText();
for (var n = 0; n < 256; n++) {
	lt.add(getLineText(n));
	lt.add(" ");
	if (1 == n % 16) {
		document.writeln("<pre class='fig'>");
		for (var i in lt.lines) {
			document.writeln(lt.lines[i]);
		}
		document.writeln("</pre>");
		document.writeln("<br clear='all'/>");
		lt = new LineText();		
	}
}
document.writeln("<pre class='fig'>");
for (var i in lt.lines) {
	document.writeln(lt.lines[i]);
}		
document.writeln("</pre>");
document.writeln("<br clear='all'/>");

document.writeln(true | (true << 1) | (true << 2));

</script>
</body>
</html>